/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package pardal.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import weka.core.Attribute;
import weka.core.Instance;
import weka.core.Instances;

/**
 * Classe que manipula o arquivo Arff.
 * @author Valdigleis
 * @version 2.0
 */
public class ArffManipulation {
    
    //Array que guarda o valor das diferentes classes que as instancias podem assumir
    private ArrayList<Nodo> nodos;
    //Ultimo level da hierarquia
    private int maxlevel;
    //instancia a serem manipuladas.
    private Instances instances;

    public ArffManipulation(Instances instances) {
        this.instances = instances;
        this.nodos = new ArrayList<>();
    }
    
    /**
     * Método responsável por pegar as diferentes Classes da hierárquia
     **/
    public void getHierarchicalClasse(){
        this.instances.setClassIndex(this.instances.numAttributes()-1);
        Attribute att = this.instances.classAttribute();
        for(int i = 0; i < att.numValues(); i++){
            String classe = att.value(i);
            Nodo newNodo = new Nodo(classe);
            this.nodos.add(newNodo);
        }
        
        for(int i = 0; i < this.nodos.size(); i++){
            setFather(this.nodos.get(i));
        }
        for(int i = 0; i < this.nodos.size(); i++){
            setSons(this.nodos.get(i));
        }
        maxLevel();
        //ordenação dos nós
        Collections.sort(this.nodos, new Comparator() {  
            @Override
            public int compare(Object o1, Object o2) {  
                Nodo p1 = (Nodo) o1;  
                Nodo p2 = (Nodo) o2;  
                return p1.getLevel() < p2.getLevel() ? -1 : (p1.getLevel() > p2.getLevel() ? +1 : 0);  
            }  
        });
        
    }
   
    /**
     * Método que seta o pai de um certo Nodo.
     * @param nodo - Nodo que será o nodo filho.
     */
    private void setFather(Nodo nodo){
        if(nodo.getLevel() > 0){
           for(int i = 0; i < this.nodos.size(); i++){
               if(this.nodos.get(i).getLevel() < nodo.getLevel() && nodo.getLabel().contains(this.nodos.get(i).getLabel())){
                   nodo.setFatherLabel(this.nodos.get(i).getLabel());
               }
           }
        }
    }
    
    /**
     * Método que seta os filhos de um certo Nodo.
     * @param nodo - Nodo que sera o pai.
     */
    private void setSons(Nodo nodo){
        ArrayList<String> sons = new ArrayList<>();
        if(nodo.getLevel() < this.maxlevel){
            for (int i = 0; i < this.nodos.size(); i++) {
                if(this.nodos.get(i).getLabel().contains(nodo.getLabel()) && !this.nodos.get(i).getLabel().equals(nodo.getLabel())){
                    String son = this.nodos.get(i).getLabel();
                    sons.add(son);
                }
            }
        }
        nodo.setSons(sons);
    }
    
    /**
     * Método que encontra a altura maxima da hierarquia
     */
    public void maxLevel(){
        this.maxlevel = 0;
        for(int i = 0; i < this.nodos.size(); i++){
            if(this.nodos.get(i).getLevel() > this.maxlevel){
                this.maxlevel = this.nodos.get(i).getLevel();
            }
        }
    }
    
    /**
     * Método que constroi o conjunto de trenamento de cada level do hierárquia.
     * 
     * @param level - Level que será montado o conjunto de treinamento.
     * @param model - Base de dados com os dados do problema.
     * @return temp - As instâncias a serem usada no treinamento local.
     */
    public Instances buildInstanceLevel(int level, Instances model){
        Instances temp = new Instances(this.instances);
        temp.delete();
        ArrayList<Nodo> aux = new ArrayList<Nodo>();
        for(int i = 0; i < this.nodos.size(); i++){
            if(this.nodos.get(i).getLevel() == level){
                Nodo newnodo = this.nodos.get(i);
                aux.add(newnodo);
            }
        }
        for(int i = 0; i < this.instances.numInstances(); i++){
            for(int j = 0; j < aux.size(); j++){
                if(this.instances.instance(i).stringValue(this.instances.classIndex()).contains(aux.get(j).getLabel()) || 
                   this.instances.instance(i).stringValue(this.instances.classIndex()).equals(aux.get(j).getLabel())){
                       //Instance instance = model.instance(i);
                       Instance instance = model.instance(i);
                       instance.setValue(instance.attribute(instance.classIndex()), instance.stringValue(instance.classIndex()));
                       temp.add(instance);
                }
            }
        }
        return temp;
    }
    
    /**
     * Método que devolve a altura da árvore
     * @return maxlevel - Altura das folhas da árvore
     */
    public int getmaxlevel(){
        return this.maxlevel;
    }

    /**
     * Método que devolve todos os Nodos da árvore
     * @return nodos - Lista com todos os nós da árvore.
     */
    public ArrayList<Nodo> getNodos() {
        return nodos;
    }
    
    /**
     * Método que devolve todos os nós folhas da hierárquia.
     * 
     * @return leafs - Uma lista com todas as folhas da árvore.
     */
    public ArrayList<Nodo> getLeaf(){
        ArrayList<Nodo> leafs = new ArrayList<>();
        for(int i = 0; i < this.nodos.size(); i++){
            if(this.nodos.get(i).getSons().isEmpty()){
                Nodo newNodo = this.nodos.get(i);
                leafs.add(newNodo);
            }
        }
        return leafs;
    }
    
}
